import { Point, Rect } from "../../../core/Layout.js";
import CanvasComponent from "../CanvasComponent.js";
import CanvasScreen from "../CanvasScreen.js";
import EventEmitter, { ClickEvent, EventType, DragEvent, EventHandler } from './EventEmitter.js'

export type HandlersOptions = {
  rect?: Rect
  draggable?: boolean
  clickable?: boolean
}

export class Handlers implements EventEmitter {
  listeners: {[type: string]: EventHandler[] }
  rect: Rect
  draggable: boolean
  clickable: boolean
  dragging: boolean
  activeTouches: Touch[]
  component?: CanvasComponent
  screen?: CanvasScreen

  constructor(options: HandlersOptions) {
    this.listeners = {}
    this.draggable = options.draggable ?? false
    this.rect = options.rect
    this.clickable = options.clickable ?? false
    this.dragging = false
    this.activeTouches = []
  }

  isInRect(point: Point) {
    if(!this.rect) return false 
    var tPoint = this.screen.transformPoint(point)
    var rect = this.rect
    var a = tPoint.x >= rect.left && tPoint.y >= rect.top
    var b = tPoint.x <= (rect.left + rect.width) && tPoint.y <= (rect.top + rect.height)
    return a && b
  }

  transformTouchList(touches: Touch[]): Touch[] {
    if(this.screen) {
      return touches.map((touch) => {
        var transformedPoint = this.screen.transformPoint(new Point(touch.clientX, touch.clientY))
        return {
          identifier: touch.identifier,
          clientX: transformedPoint.x,
          clientY: transformedPoint.y,
          force: touch.force,
          pageX: touch.pageX,
          pageY: touch.pageY,
          radiusX: touch.radiusX
        } as Touch
      })
    }
    return []
  }

  filterTouches(touchList: TouchList, mustInRect: boolean = false) {
    var touches: Touch[] = []
    var identifierList = this.activeTouches.map((item) => item.identifier)
    for (let index = 0; index < touchList.length; index++) {
      var touch = touchList.item(index)
      if(mustInRect && this.isInRect(new Point(touch.clientX, touch.clientY))) {
        touches.push(touch)
      } else if(!mustInRect) {
        if(identifierList.includes(touch.identifier)) touches.push(touch)
      }
    }
    return this.transformTouchList(touches)
  }

  touchStart(evt: TouchEvent) {
    if(this.draggable) {
      var touches = this.filterTouches(evt.changedTouches, true)
      touches.forEach((touch) => {
        this.dragging = true
        var handlers = this.listeners['dragStart'] || []
        handlers.forEach((handler: EventHandler) => {
          handler(DragEvent.startWith(touch))
        })
      })
      if(touches.length > 0) {
        this.activeTouches = touches || []
      }
    }
    if(this.clickable) {
      var touches = this.filterTouches(evt.changedTouches, true)
      if(touches.length === 1) {
        var touch = touches[0]
        if(touch.force) {
          var handlers = this.listeners['click'] || []
          handlers.forEach((handler: EventHandler) => {
            handler(ClickEvent.with(touch))
          })
        }
      }
    }
  }

  touchEnd(evt: TouchEvent) {
    if(this.draggable && this.dragging) {
      var touches = this.filterTouches(evt.changedTouches)
      touches.forEach((touch) => {
        var handlers = this.listeners['dragEnd'] || []
        handlers.forEach((handler: EventHandler) => {
          handler(DragEvent.endWith(touch))
        })
      })
      if(touches.length > 0) {
        this.dragging = false
      }
    }
  }
  touchCancel(evt: TouchEvent) {
    this.touchEnd(evt)
  }

  touchMove(evt: TouchEvent) {
    if(this.draggable && this.dragging) {
      var touches = this.filterTouches(evt.touches)
      touches.forEach((touch) => {
        var handlers = this.listeners['drag'] || []
        handlers.forEach((handler: EventHandler) => {
          handler(DragEvent.dragWith(touch))
        })
      })
    }
  }

  addEventListener(type: EventType, listener: EventHandler, options?: boolean | AddEventListenerOptions): void {
    if(!this.listeners[type]) this.listeners[type] = []
    this.listeners[type].push(listener)
  }

  removeEventListener(type: EventType, listener: EventHandler, options?: boolean | EventListenerOptions): void {
    if(!this.listeners[type]) this.listeners[type] = []
    this.listeners[type] = this.listeners[type].filter(item => item === listener)
  }

  destroy() {
    this.listeners = {}
  }
}